package models;

import javax.persistence.*;

import org.hibernate.annotations.Fetch;

import java.util.*;

import play.*;
import play.db.jpa.*;
import play.libs.*;
import play.data.validation.*;

import utils.collection.CollectionUtils;
import utils.collection.Predicate;

/**
 * User class for the normal members  
 * 
 * member fields needed for register action 
 *  userName, password, email
 *  
 * subclasses:
 *  RetailUser, ClubUser
 */

@Entity
public class User extends Model {
	
	
	// user type indicator 
	public enum Role {
		CLUB, RETAILER;
	}
	
	public String userName;
	public String password;
	public String email;
	public String desc;
	public String name;
	
	@Transient
	private Role role;
	
	
	@OneToMany(mappedBy="discountCreator", cascade=CascadeType.ALL, fetch=FetchType.EAGER)
	public List<Discount> discountsCreated;
	
	@OneToMany(mappedBy="discountReceiver", cascade=CascadeType.ALL, fetch=FetchType.EAGER)
	public List<Discount> discountsReceived;
	
	public User (String userName, String password, String email, String name, String desc) {
		this(userName, password, email, name);
		this.desc = desc;
	}
	
	public User (String userName, String password, String email, String name) {
		this.userName = userName;
		this.password = password;
		this.email = email;
		this.name = name;

		this.discountsCreated = new ArrayList<Discount>();
		this.discountsReceived = new ArrayList<Discount>();
	}
	
	/**
	 * Returns list of discounts created or received that are not expired.
	 * 
	 * @return list of discounts
	 */
	public List<Discount> getActiveDiscounts() {
		return selectDiscounts(new Predicate<Discount>() {
			Date now = new Date();
			
			public boolean apply(Discount discount) {
				return discount.expirationDate.after(now) && discount.status == Status.ACCEPT;
			}
		}, discountsCreated, discountsReceived);
	}
	
	public List<Discount> getActiveDiscountsWith(final User user) {
		return selectDiscounts(new Predicate<Discount>() {
			Date now = new Date();
			
			public boolean apply(Discount discount) {
				return discount.expirationDate.after(now) && 
						discount.status == Status.ACCEPT && 
						(discount.discountCreator == user || discount.discountReceiver == user);
			}
		}, discountsCreated, discountsReceived);
	}	
	
	public boolean checkPassword(String pwd) {
	    return this.password.equals(pwd);
	}
	
	public static User findByUsername(String user_name) {
	    return find("userName", user_name).first();
	}
	
	public static User findById(long userId) {
	    return find("id", userId).first();
	}
	
	public String toString() {
		return this.userName;
	}

	/**
	 * Returns list of discounts received that are not expired and are waiting 
	 * for approval
	 * 
	 * @return list of discounts
	 */
	public List<Discount> getDiscountsWaitingApproval() {
		return selectDiscounts(new Predicate<Discount>() {
			Date now = new Date();
			
			public boolean apply(Discount discount) {
				return discount.expirationDate.after(now) && discount.status == Status.PROCESS;
			}
		}, discountsReceived);
	}
	
	/**
	 * Helper function for selecting discounts
	 * 
	 * @param predicate
	 * @param collections
	 * @return
	 */
	private List<Discount> selectDiscounts(Predicate<Discount> predicate, List<Discount>... collections) {
		List<Discount> discounts = new ArrayList<Discount>();
		
		for (List<Discount> discountCollection : collections) {
			discounts.addAll(CollectionUtils.select(discountCollection, predicate));
		}
		
		return discounts;
	}
	
	public <T> List<T> getActivePartners() {
		List<T> partners = new ArrayList<T>();
		
		List<Discount> activeDiscounts = getActiveDiscounts();
		
		for (models.Discount discount : activeDiscounts) {
			partners.add((T) discount.otherParty(this));
		}
		
		return partners;
	}
	
	public User createDiscount(String description, Date expirationDate, String url, User discountReceiver) {
		Discount newDiscount = new Discount(description, expirationDate, url, this, discountReceiver);
		this.discountsCreated.add(newDiscount);
		this.save();
		discountReceiver.discountsReceived.add(newDiscount);
		discountReceiver.save();
		return this;
	}
	
	public Role getUserRole() {
		if(role == null) {
			if(this instanceof ClubUser) {
				role = Role.CLUB;
			} else if (this instanceof RetailUser) {
				role = Role.RETAILER;
			} else {
				role = null;
			}
		}
		return role;
	}
}
